/**
 *  @file SDLResourceManager.cpp
 *
 *  @date 25-nov-2009
 *  @author Ives van der Flaas
 */

#include <sstream>
#include <cassert>
#include <SDL_image.h>
#include "SDLSurfaceDeleter.h"
#include "SDLFontDeleter.h"
#include "SDLResourceManager.h"
#include "SDLException.h"
#include "SDLMusicDeleter.h"
#include "SDLChunkDeleter.h"

namespace si_sdl
{
std::tr1::shared_ptr<TTF_Font> SDLResourceManager::getFont(
														std::string fontLocation,
														int sizeInPt)
{
	std::string coded = encode(fontLocation, sizeInPt);
	if(fFonts.count(coded) == 0)
	{
		prefetchNextTime(new SDLFontPrefetchNode(fontLocation,sizeInPt));

		// not in the map, so add it...
		fFonts[coded]
		   = std::tr1::shared_ptr<TTF_Font>(
				   TTF_OpenFont(fontLocation.c_str(), sizeInPt),
				   SDLFontDeleter());

		// if font failed to load
		if (!(fFonts[coded].get()))
			throw SDLException(
				std::string("Unable to load font: ") +
				std::string(fontLocation) +
				std::string(" because of error: ") +
				std::string(TTF_GetError()) +
				std::string("\n"));
	}

	assert(fFonts.count(coded) == 1);

	return fFonts[coded];
}

std::tr1::shared_ptr<SDL_Surface> si_sdl::SDLResourceManager::getImage(std::string location, Color c)
{
	std::string coded = encodeImage(location, false);

	if(fSurfaces.count(coded) == 0)
	{
		// not in the map, so add it...
		SDL_Surface* original = IMG_Load(location.c_str());

		if(original == 0)
			throw SDLException(
					std::string("Cannot load image file ")
						+ location
						+ ":"
						+ SDL_GetError()
						+ "\n"
					);

		SDL_Surface* optimized = SDL_DisplayFormat(original);
		SDL_FreeSurface(original);

		SDL_SetColorKey(
				optimized,
				SDL_SRCCOLORKEY,
				SDL_MapRGB(optimized->format, c.getR(), c.getG(), c.getB()));

		//put it in the map...
		fSurfaces[coded] = std::tr1::shared_ptr<SDL_Surface>(optimized, SDLSurfaceDeleter());
	}

	assert(fSurfaces.count(coded) == 1);

	// now return a COPY of this surface...
	return std::tr1::shared_ptr<SDL_Surface>(
			SDL_ConvertSurface(
					fSurfaces[coded].get(),
					fSurfaces[coded].get()->format,
					SDL_SWSURFACE),
			SDLSurfaceDeleter());
}

std::tr1::shared_ptr<SDL_Surface> si_sdl::SDLResourceManager::getImageAlpha(std::string location)
{
	std::string coded = encodeImage(location, true);
	if(fSurfaces.count(coded) == 0)
	{
		prefetchNextTime(new SDLImagePrefetchNode(location));

		// not in the map, so add it...
		SDL_Surface* original = IMG_Load(location.c_str());

		if(original == 0)
			throw SDLException(
					std::string("Cannot load image file ")
						+ location
						+ ":"
						+ SDL_GetError()
						+ "\n"
					);

		SDL_Surface* optimized = SDL_DisplayFormatAlpha(original);
		SDL_FreeSurface(original);

		//put it in the map...
		fSurfaces[coded] = std::tr1::shared_ptr<SDL_Surface>(optimized, SDLSurfaceDeleter());
	}

	assert(fSurfaces.count(coded) == 1);

	// now return a COPY of this surface...
	return std::tr1::shared_ptr<SDL_Surface>(
			SDL_ConvertSurface(
					fSurfaces[coded].get(),
					fSurfaces[coded].get()->format,
					SDL_SWSURFACE),
			SDLSurfaceDeleter());
}


SDLResourceManager::SDLResourceManager()
{
	// Prefetch!
	TiXmlDocument doc("config/sdlprefetch.xml");
	if(!doc.LoadFile())
		return;

	TiXmlDeclaration* decl = doc.FirstChild()->ToDeclaration();
	assert(std::string(decl->Version()) == std::string("1.0"));

	TiXmlElement* root = doc.FirstChildElement("resources");

	// prefetch images
	TiXmlElement* node = root->FirstChildElement("image");
	while(node)
	{
		std::string file = node->GetText();
		getImageAlpha(file);
		node = node->NextSiblingElement("image");
	}

	//prefetch fonts
	node = root->FirstChildElement("font");
	while(node)
	{
		std::string file = node->GetText();
		int size;
		node->Attribute("size", &size);

		getFont(file, size);

		node = node->NextSiblingElement("font");
	}

	//prefetch chunks
	node = root->FirstChildElement("chunk");
	while(node)
	{
		std::string file = node->GetText();
		getChunk(file);

		node = node->NextSiblingElement("chunk");
	}

	//prefetch music
	node = root->FirstChildElement("music");
	while(node)
	{
		std::string file = node->GetText();
		getMusic(file);
		node = node->NextSiblingElement("music");
	}

}

SDLResourceManager::~SDLResourceManager()
{
	// If I don't do this, the map will, but doing it gives me a warm and fussy feeling inside...
	fFonts.clear();
	fSurfaces.clear();
	fMusic.clear();
	fChunks.clear();

	// Now, write the prefetch file...
	TiXmlDocument doc;

	TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "","");

	doc.LinkEndChild(decl);

	TiXmlElement* root = new TiXmlElement("resources");

	doc.LinkEndChild(root);

	for(std::list<std::tr1::shared_ptr<SDLPrefetchNode> >::iterator it = fPrefetchNodes.begin();
			it != fPrefetchNodes.end();
			it++)
	{
		(*it)->write(root);
	}


	doc.SaveFile("config/sdlprefetch.xml");
}

std::string SDLResourceManager::encode(std::string filename, int pts) const
{
	std::stringstream s;
	s << pts;
	std::string ptString;
	s >> ptString;

	return filename + ptString;

}

std::string SDLResourceManager::encodeImage(std::string filename, bool transparency) const
{
	std::string b = transparency ? "1" : "0";
	return filename+b;

}

std::tr1::shared_ptr<Mix_Music> SDLResourceManager::getMusic(std::string location)
{
	prefetchNextTime(new SDLMusicPrefetchNode(location));

	if(fMusic.count(location) == 0)
	{
		// not in the map, so add it...
		Mix_Music* music = Mix_LoadMUS(location.c_str());

		if(music == 0)
			throw SDLException(
					std::string("Cannot load audio file ")
						+ location
						+ ":"
						+ SDL_GetError()
						+ "\n"
					);


		//put it in the map...
		fMusic[location] = std::tr1::shared_ptr<Mix_Music>(music, SDLMusicDeleter());
	}

	assert(fMusic.count(location) == 1);

	// now return a COPY of this surface...
	return fMusic[location];
}

std::tr1::shared_ptr<Mix_Chunk> SDLResourceManager::getChunk(std::string location)
{

	if(fChunks.count(location) == 0)
	{
		prefetchNextTime(new SDLChunkPrefetchNode(location));

		// not in the map, so add it...
		Mix_Chunk* music = Mix_LoadWAV(location.c_str());

		if(music == 0)
			throw SDLException(
					std::string("Cannot load WAV file ")
						+ location
						+ ":"
						+ SDL_GetError()
						+ "\n"
					);


		//put it in the map...
		fChunks[location] = std::tr1::shared_ptr<Mix_Chunk>(music, SDLChunkDeleter());
	}

	assert(fChunks.count(location) == 1);

	// now return a COPY of this surface...
	return fChunks[location];
}

void SDLResourceManager::prefetchNextTime(SDLPrefetchNode* prefetchNode)
{
	fPrefetchNodes.push_back(std::tr1::shared_ptr<SDLPrefetchNode>(prefetchNode));
}
}
